package com.acme.hashmap;

import com.acme.hash.HashConvolution;
import com.acme.hash.IntHashFunction;
import com.acme.hash.SimpleIntHashFunctionImpl;

/**
 * @author Gregory Sobko
 */
public class DirectAddressedHashMapIntToInt implements MapIntToInt {
    private final int INITIAL_CAPACITY = 10;
    /** Should be in range (0, 1) */
    private final double INCREASE_THRESHOLD = 0.5;

    private final int NULL_ELEMENT = 0;
    private final int NOT_NULL_ELEMENT = 1;


    private int capacity = INITIAL_CAPACITY;
    private int size;
    private int[] keys;
    private int[] values;
    private byte[] nulls;
    private HashConvolution hashFunction;

    public DirectAddressedHashMapIntToInt() {
        this(new SimpleIntHashFunctionImpl());
    }

    public DirectAddressedHashMapIntToInt(IntHashFunction hashFunction) {
        this.hashFunction = new HashConvolution(hashFunction);
        keys = new int[capacity];
        values = new int[capacity];
        nulls = new byte[capacity];
        this.hashFunction.setDimension(capacity);
    }

    @Override
    public Integer put(int key, int value) {
        Integer previous;
        if((previous = setElement(key, value)) == null){
            incrementSize();
        }
        return previous;
    }

    @Override
    public Integer get(int key) {
        int index = hashFunction.hash(key);
        while (nulls[index] != NULL_ELEMENT){
            if(keys[index] == key){
                return values[index];
            }
            index++;
            if(index >= keys.length){
                index=0;
            }
        }
        return null;
    }

    @Override
    public boolean contains(int key) {
        return get(key) != null ;
    }

    @Override
    public Integer remove(int key) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void clear() {
        keys = new int[capacity];
        values = new int[capacity];
        nulls = new byte[capacity];
        size = 0;
    }

    @Override
    public int size() {
        return size;
    }


    private Integer setElement(int key, int value){
        Integer previous;
        int index = hashFunction.hash(key);
        while (nulls[index] != NULL_ELEMENT){
            if(keys[index]==key){
                previous = values[index];
                values[index]=value;
                return previous;
            }
            index++;
            if(index>=keys.length){
                index=0;
            }
        }
        nulls[index] = NOT_NULL_ELEMENT;
        keys[index] = key;
        values[index] = value;
        return null;
    }

    private void incrementSize(){
        size++;
        if(size>=INCREASE_THRESHOLD*capacity){
            increaseCapacity(1.2);
        }
    }

    private void increaseCapacity(double times){
        int[] copyFromKeys  = keys;
        int[] copyFromValues  = values;
        byte[] copyFromNulls  = nulls;
        capacity *= times;
        hashFunction.setDimension(capacity);
        keys = new int[capacity];
        values = new int[capacity];
        nulls = new byte[capacity];
        for (int i = 0; i < copyFromKeys.length; i++) {
            if(copyFromNulls[i] != NULL_ELEMENT){
                setElement(copyFromKeys[i], copyFromValues[i]);
            }
        }
    }

}
